/**
 * 视频帧录制控制器，用于特定fps下实时帧录制，并转化为canvas
 */
class FrameController {
  /**
   * @param {HTMLVideoElement} videoElement - 视频元素
   * @param {HTMLVideoElement} canvasElement - canvas元素
   * @param {number} fps - 帧率 (默认25)
   */
  constructor(videoElement, canvasElement, fps = 25) {
    this.video = videoElement;
    this.canvas = canvasElement;
    this.fps = fps;
    this.frames = [];
    this.currentFrameIndex = -1;
    this.isRecording = false;
    this.isPaused = false;
    this.recordingInterval = null;
    this.frameDuration = 1000 / fps;
    this.lastCaptureTime = 0;
  }

  /**
   * 开始录制帧
   */
  startRecording() {
    if (this.isRecording && !this.isPaused) return;
    // 如果是暂停后继续录制
    if (this.isPaused) {
      this.isPaused = false;
      this._resumeRecording();
      return;
    }
    // 全新开始录制
    this.isRecording = true;
    this.isPaused = false;
    this.frames = []; // 重置帧数组
    this.currentFrameIndex = -1;
    this.lastCaptureTime = 0;
    // 立即记录第一帧
    this._captureFrame();
    // 设置定时器按指定FPS录制
    this._startRecordingInterval();
  }

  /**
   * 暂停录制帧
   */
  pauseRecording() {
    if (!this.isRecording || this.isPaused) return;
    this.isPaused = true;
    clearInterval(this.recordingInterval);
    this.recordingInterval = null;
  }

  /**
   * 停止录制帧
   */
  stopRecording() {
    if (!this.isRecording) return;
    this.isRecording = false;
    this.isPaused = false;
    clearInterval(this.recordingInterval);
    this.recordingInterval = null;
  }

  /**
   * 跳转到下一帧
   * @returns {Promise<ImageData|null>} 下一帧的图像数据或null(如果没有下一帧)
   */
  async nextFrame() {
    if (this.currentFrameIndex >= this.frames.length - 1) {
      return null;
    }
    this.currentFrameIndex++;
    return this._displayFrame(this.currentFrameIndex);
  }

  /**
   * 跳转到上一帧
   * @returns {Promise<ImageData|null>} 上一帧的图像数据或null(如果没有上一帧)
   */
  async prevFrame() {
    if (this.currentFrameIndex <= 0) {
      return null;
    }
    this.currentFrameIndex--;
    return this._displayFrame(this.currentFrameIndex);
  }

  /**
   * 跳转到指定帧
   * @param {number} index - 帧索引
   * @returns {Promise<ImageData|null>} 帧的图像数据或null(如果索引无效)
   */
  async gotoFrame(index) {
    if (index < 0 || index >= this.frames.length) {
      return null;
    }
    this.currentFrameIndex = index;
    return this._displayFrame(this.currentFrameIndex);
  }

  /**
   * 获取当前帧索引
   * @returns {number} 当前帧索引
   */
  getCurrentFrameIndex() {
    return this.currentFrameIndex;
  }

  /**
   * 获取总帧数
   * @returns {number} 总帧数
   */
  getTotalFrames() {
    return this.frames.length;
  }

  /**
   * 获取录制状态
   * @returns {Object} 包含isRecording和isPaused的状态对象
   */
  getRecordingStatus() {
    return {
      isRecording: this.isRecording,
      isPaused: this.isPaused
    };
  }

  /**
   * 内部方法：启动录制定时器
   */
  _startRecordingInterval() {
    this.recordingInterval = setInterval(() => {
      this._captureFrame();
    }, this.frameDuration);
  }

  /**
   * 内部方法：恢复录制（从暂停状态继续）
   */
  _resumeRecording() {
    // 计算暂停期间错过的帧数
    const now = performance.now();
    const missedTime = now - this.lastCaptureTime;
    const missedFrames = Math.floor(missedTime / this.frameDuration);
    // 如果需要补偿错过的帧
    if (missedFrames > 0) {
      for (let i = 0; i < missedFrames; i++) {
        this._captureFrame();
      }
    }
    // 重新开始定时器
    this._startRecordingInterval();
  }

  /**
   * 内部方法：捕获当前帧
   */
  _captureFrame() {
    const canvas = document.createElement('canvas');
    canvas.width = (this.video.videoWidth * this.video.offsetHeight) / this.video.videoHeight;
    canvas.height = this.video.offsetHeight;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(this.video, 0, 0, canvas.width, canvas.height);
    // 存储帧数据
    this.frames.push({
      timestamp: this.video.currentTime, // 对应video播放时间
      imageData: canvas.toDataURL('image/jpeg'), // jpeg图片
      frameId: `${this.video.currentTime}-${this.frames.length}` // 帧id（索引值）
    });
    this.currentFrameIndex = this.frames.length - 1;
    this.lastCaptureTime = performance.now();
  }

  /**
   * 内部方法：显示指定帧
   * @param {number} index - 帧索引
   * @returns {Promise<ImageData>} 帧的图像数据
   */
  async _displayFrame(index) {
    const imageData = this.frames[index].imageData
    const ctx = this.canvas.getContext('2d');
    const image = new Image();
    image.onload = () => {
      ctx.drawImage(image, 0, 0, this.canvas.width, this.canvas.height);
    }
    image.src = imageData;
  }

  /**
   * 销毁控制器，释放资源
   */
  destroy() {
    this.stopRecording();
    this.frames = [];
    this.video = null;
  }
}

export default FrameController;
